package com.koron.common.core.exception;

import cn.hutool.core.util.StrUtil;
import com.koron.bean.base.Response;
import com.koron.common.core.util.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartException;

import javax.ejb.DuplicateKeyException;
import javax.servlet.MultipartConfigElement;
import java.lang.reflect.InvocationTargetException;
import java.sql.DataTruncation;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@ControllerAdvice
public class DefaultGlobalExceptionHandlerAdvice {

    @Autowired
    MultipartConfigElement multipartConfig;

    @ResponseBody
    @ExceptionHandler(value = {MultipartException.class})
    public Response multipartException(MultipartException ex) {
        log.error("upload file size limit:{}", ex.getMessage());
        return Response.fail("单个文件不得超过" + multipartConfig.getMaxFileSize() / (1024 * 1024) + "M, 请求不得超过" + multipartConfig.getMaxRequestSize() / (1024 * 1024) + "M");
    }

    @ResponseBody
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public Response missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.error("missing servlet request parameter exception:{}", ex.getMessage());
        return Response.fail("missing servlet request parameter exception");
    }

    @ResponseBody
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public Response serviceException(MethodArgumentNotValidException ex) {
        log.error("MethodArgumentNotValidException:{},{}", ex.getMessage(), ex);
        return handlerBindingResult(ex.getBindingResult());
    }

    private Response handlerBindingResult(BindingResult bindingResult) {
        String message = "";
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                message = fieldError.getField() + fieldError.getDefaultMessage();
            }
        }
        log.error(message);
        return Response.fail(message);
    }

    @ResponseBody
    @ExceptionHandler(value = {BaseException.class})
    public Response baseException(BaseException ex) {
        log.error("BaseException:{}", ex.getMessage());
        return Response.fail(ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {BindException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response BindException(BindException ex) {
        log.error("BindException:{},{}", ex.getMessage(), ex);
        return handlerBindingResult(ex.getBindingResult());
    }

    @ResponseBody
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response HttpMessageNotReadableException(Exception ex) {
        log.error("HttpMessageNotReadableException:{}", (((HttpMessageNotReadableException) ex).getMessage()));
        return Response.fail(ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {DuplicateKeyException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response DuplicateKeyException(Exception ex) {
        log.error("DuplicateKeyException:{}", ex.getMessage());
//        return Response.fail("唯一性校验不通过");
        String errorformat = CommonUtils.msgFormat("唯一性校验不通过", "唯一性校驗不通過", "Uniqueness verification failed");
        return Response.fail(CommonUtils.converMsgByLanguage(errorformat));
    }

    @ResponseBody
    @ExceptionHandler(value = {TooManyResultsException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response TooManyResultsException(Exception ex) {
        log.error("TooManyResultsException:{}", ex.getMessage());
        return Response.fail("期待一条返回多条异常");
    }

    @ResponseBody
    @ExceptionHandler(value = {Exception.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response exception(Exception ex) {
        log.error("Exception", ex);
        return Response.fail(ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {RuntimeException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response runtimeException(RuntimeException ex) throws Throwable {
        log.error("RuntimeException", ex);

        if (ex.getCause() != null) {
            if (ex.getCause() instanceof IllegalArgumentException) {
                return illegalArgumentException((IllegalArgumentException)ex.getCause());
            } else if (ex.getCause() instanceof TooManyResultsException) {
                return Response.fail(ex.getCause().getMessage());
            } else if (ex.getCause() instanceof DataTruncation) {
                return sqlDataTruncationException((DataTruncation)ex.getCause());
            } else if (ex.getCause() instanceof InvocationTargetException) {
                return runtimeException((RuntimeException)((InvocationTargetException) ex.getCause()).getTargetException());
            } else {
                return Response.fail(ex.getCause().getMessage());
            }
        }

        if (ex instanceof IllegalArgumentException) {
            return illegalArgumentException((IllegalArgumentException)ex);
        }
        return Response.fail(ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {Throwable.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response throwable(Throwable e) {
        log.error(e.getMessage(), e);
        return Response.fail(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response illegalArgumentException(IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        Response<String> response = new Response<>();
        response.setCode(5001);
        response.setDescription(e.getMessage());
        return Response.fail(CommonUtils.converMsgByLanguage(e.getMessage()));
    }

    @ResponseBody
    @ExceptionHandler(value = {SQLIntegrityConstraintViolationException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response sqlIntegrityConstraintViolationException(IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        return Response.fail(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = {DataTruncation.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Response sqlDataTruncationException(DataTruncation e) {
        log.error(e.getMessage(), e);
        Pattern pattern = Pattern.compile("column '(.+)'");
        Matcher matcher = pattern.matcher(e.getMessage());
        String group = StrUtil.toCamelCase(matcher.find() ? matcher.group(1) : "");
        Response<String> response = new Response<>();
        response.setCode(5002);
        response.setData(group);
        response.setDescription("字段"+ group +"过长");
        return response;
    }

}
